package com.jboltai.capability.functioncall;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Singleton;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.jboltai.resource.functioncall.FunctionResource;
import com.jboltai.resource.functioncall.FunctionResourceCenter;
import com.jboltai.util.http.HttpMethod;
import com.jboltai.util.http.HttpUtil;
import com.jboltai.util.http.RequestInstance;
import okhttp3.Response;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;


/**
 * 匹配的function call结果
 */
public class FuncCallResult implements Serializable {

    /**
     * 匹配的function name
     */
    private String function;

    /**
     *
     */
    private String functionId;

    /**
     * 从prompt中解析的function参数值
     */
    private Map<String, Object> parameters;

    public FuncCallResult() {
    }

    public FuncCallResult(String function, Map<String, Object> parameters) {
        this.function = function;
        this.parameters = parameters;
    }

    public static FuncCallResult fromJson(String content) {
        if (StrUtil.isBlank(content)) {
            return null;
        }
        JSONObject jsonObject = JSONObject.parseObject(content);
        if (!jsonObject.containsKey("function")) {
            return null;
        }
        String function = jsonObject.getString("function");
        if (StrUtil.isBlank(function)) {
            return null;
        }
        FuncCallResult functionCallResult = new FuncCallResult();
        functionCallResult.setFunction(function);
        if (jsonObject.containsKey("parameters")) {
            Object parameters = jsonObject.get("parameters");
            //判断如果不是null 并且是JSONObject
            if (parameters != null && parameters instanceof Map) {
                functionCallResult.setParameters(jsonObject.getJSONObject("parameters").getInnerMap());
            }
        }
        return functionCallResult;
    }

    public String getFunction() {
        return function;
    }

    public void setFunction(String function) {
        this.function = function;
    }

    public Map<String, Object> getParameters() {
        return parameters;
    }

    public void setParameters(Map<String, Object> parameters) {

        this.parameters = parameters;
    }

    public String getFunctionId() {
        return functionId;
    }

    public void setFunctionId(String functionId) {
        this.functionId = functionId;
    }

    /**
     * 数据convertype
     * @param paramMap
     * @param name
     * @param paramType
     * @return
     * @throws ParseException
     */
    private Object processFunctionParamType(Map<String, Object> paramMap, String name, Class<?> paramType) throws ParseException {
        if (!paramMap.containsKey(name)) {
            return null;
        }
        Object value = paramMap.get(name);
        if (value == null) {
            return null;
        }
        if (value.getClass() == paramType) {
            return value;
        }
        return Convert.convert(paramType, value);
    }

    /**
     * 调用本地注册的Function
     * @param clazz
     * @param clazzInst
     * @param methodName
     * @param paramCount
     * @return
     * @throws Exception
     */
    public Object invokeNative(Class<?> clazz, Object clazzInst, String methodName, int paramCount) throws Exception {
        FunctionResource functionResource = FunctionResourceCenter.getById(functionId);
        if (functionResource == null) {
            throw new RuntimeException("执行function:[" + function + "]当前Function资源不可用");
        }
        if (parameters == null) {
            parameters = new HashMap<>();
        }
        Object returnValue = null;
        if (paramCount == 0) {
            Method method = ClassUtil.getPublicMethod(clazz, methodName);
            if (method == null) {
                throw new RuntimeException("AI_Function[" + function + "]对应的Service 类[" + clazz.getName() + "]中没有找到[" + methodName + "]方法");
            }
            try {
                returnValue = method.invoke(clazzInst);
            } catch (Exception e) {
                throw new RuntimeException("AI_Function[" + function + "]调用失败:" + StrUtil.blankToDefault(e.getMessage(), e.getCause().getMessage()));
            }

        } else {
            List<FunctionParam> paramList = functionResource.getParameters();
            if (paramList == null || paramList.size() != paramCount) {
                throw new RuntimeException("AI_Function[" + function + "]参数定义设置异常");
            }
            Object[] paramsValues = new Object[paramCount];
            Class<?>[] paramTypes = new Class<?>[paramCount];
            for (int i = 0; i < paramCount; i++) {
                FunctionParam param = paramList.get(i);
                paramTypes[i] = Singleton.get(param.getType(), () -> ClassUtil.loadClass(param.getType()));
                if (paramTypes[i] == null) {
                    throw new RuntimeException("AI_Function[" + function + "]参数[" + param.getName() + "]类型[" + param.getType() + "]不存在");
                }
                try {
                    paramsValues[i] = processFunctionParamType(parameters, param.getName(), paramTypes[i]);
                } catch (Exception e) {
                    throw new RuntimeException("AI_Function[" + function + "]参数定义设置异常");
                }
            }
            //找到具体方法method
            Method method = ClassUtil.getPublicMethod(clazz, methodName, paramTypes);
            if (method == null) {
                throw new RuntimeException("AI_Function[" + function + "]对应的Service 类[" + clazz.getName() + "]中没有找到[" + methodName + "]方法");
            }
            //执行调用它
            try {
                returnValue = method.invoke(clazzInst, paramsValues);
            } catch (Exception e) {
                throw new RuntimeException("AI_Function[" + function + "]调用失败:" + StrUtil.blankToDefault(e.getMessage(), e.getCause().getMessage()));
            }
        }
        return returnValue;
    }

    /**
     * 执行API类function
     * @param apiUrl
     * @param httpMethod
     * @param headers
     * @param params
     * @param successHandler
     * @param failHandler
     */
    public void invokeApi(String apiUrl, HttpMethod httpMethod, Map<String, String> headers, Map<String, Object> params, Consumer<Response> successHandler, Consumer<Throwable> failHandler) {
        RequestInstance request = null;
        switch (httpMethod) {
            case GET:
                request = HttpUtil.get(apiUrl);
                break;
            case POST:
                request = HttpUtil.post(apiUrl);
                break;
        }
        if (request == null) {
            failHandler.accept(new RuntimeException("httpMethod must be POST or GET"));
            return;
        }

        if(params!=null && !params.isEmpty()){
            switch (httpMethod) {
                case GET:
                    request.addAllQueryParam(params);
                    break;
                case POST:
                    request.addAllJsonParam(params);
                    break;
            }
        }

        //if(contentTypeIsJson){
        //    request.setContentTypeToJson();
        //}
        if(headers!=null && !headers.isEmpty()){
            request.setHeaders(headers);
        }
        request.asyncSend(successHandler,failHandler);
    }
}
